package com.dxysun.spring.servlet.v4ioc;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.dxysun.annotation.DXYRequestMapping;
import com.dxysun.annotation.DXYRequestParam;
import com.dxysun.spring.context.DXYApplicationContext;

public class DxyDispatchServlet extends HttpServlet
{
	private List<Handler> handlerList = new ArrayList<>();

	private DXYApplicationContext applicationContext;

	@Override
	public void init(ServletConfig config) throws ServletException
	{
		super.init(config);

		applicationContext = new DXYApplicationContext(config.getInitParameter("contextConfigLocation"));
		// 5、初始化handlerMapping
		doInitHandleMapping();

		System.out.println("DXY MVC Framework is init");
	}

	private void doInitHandleMapping()
	{
		for (String beanName : applicationContext.getBeanDefinitionNames())
		{
			Object object = applicationContext.getBean(beanName);
			if (object == null)
			{
				continue;
			}
			Class<?> clazz = object.getClass();
			if (clazz.isAnnotationPresent(DXYRequestMapping.class))
			{
				DXYRequestMapping dxyRequestMapping = clazz.getAnnotation(DXYRequestMapping.class);
				String baseUrl = dxyRequestMapping.value();
				Method[] methods = clazz.getDeclaredMethods();
				for (Method method : methods)
				{
					if (method.isAnnotationPresent(DXYRequestMapping.class))
					{
						DXYRequestMapping dxyMethodRequestMapping = method.getAnnotation(DXYRequestMapping.class);
						String url = baseUrl + "/" + dxyMethodRequestMapping.value();
						Pattern pattern = Pattern.compile(url.replaceAll("/+", "/"));
						handlerList.add(new Handler(
								applicationContext.getBean(toFistLowerCase(method.getDeclaringClass().getSimpleName())),
								method, pattern));
						System.out.println("Mapped " + url + "," + method);
					}
				}
			}
		}
	}

	private String toFistLowerCase(String beanName)
	{
		char[] chars = beanName.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);
	}

	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
	{
		doPost(req, resp);
	}

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{
		try
		{
			doDispatch(request, response);
		}
		catch (Exception e)
		{
			e.printStackTrace();
			response.getWriter().write("500 Exception " + Arrays.toString(e.getStackTrace()));
		}
	}

	public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception
	{
		String url = request.getRequestURI();
		String contextPath = request.getContextPath();
		url = url.replace(contextPath, "").replaceAll("/+", "/");
		Handler handler = getHandler(url);
		if (handler == null)
		{
			response.getWriter().write(url + " 404 Not Found!!");
			return;
		}
		Map<String, Integer> paramIndexMapping = handler.paramIndexMapping;
		Class<?>[] parameterTypes = handler.method.getParameterTypes();
		Object[] paramValues = new Object[parameterTypes.length];

		paramValues[paramIndexMapping.get(HttpServletRequest.class.getSimpleName())] = request;
		paramValues[paramIndexMapping.get(HttpServletResponse.class.getSimpleName())] = response;

		Map<String, String[]> paramsMap = request.getParameterMap();

		for (Map.Entry<String, String[]> param : paramsMap.entrySet())
		{
			// [1 , 2 , 3]
			String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll("\\s", "");

			//如果找到匹配的对象，则开始填充参数值
			if (!handler.paramIndexMapping.containsKey(param.getKey()))
			{
				continue;
			}
			int index = handler.paramIndexMapping.get(param.getKey());
			paramValues[index] = convert(parameterTypes[index], value);
		}

		handler.method.invoke(handler.controller, paramValues);
	}

	public Handler getHandler(String url)
	{
		for (Handler handler : handlerList)
		{
			if (handler.pattern.matcher(url).matches())
			{
				return handler;
			}
		}
		return null;
	}

	//url传过来的参数都是String类型的，HTTP是基于字符串协议
	//只需要把String转换为任意类型就好
	private Object convert(Class<?> type, String value)
	{
		if (Integer.class == type)
		{
			return Integer.valueOf(value);
		}
		//如果还有double或者其他类型，继续加if
		//这时候，我们应该想到策略模式了
		//在这里暂时不实现，希望小伙伴自己来实现
		return value;
	}

	private class Handler
	{
		Object controller;
		Method method;
		Pattern pattern; //${} url占位符解析
		Map<String, Integer> paramIndexMapping; //参数顺序

		public Handler(Object controller, Method method, Pattern pattern)
		{
			this.controller = controller;
			this.method = method;
			this.pattern = pattern;
			paramIndexMapping = new HashMap<>();
			initParamIndexMapping(method);

		}

		private void initParamIndexMapping(Method method)
		{
			Annotation[][] annotations = method.getParameterAnnotations();
			for (int i = 0; i < annotations.length; i++)
			{
				for (Annotation annotation : annotations[i])
				{
					if (annotation instanceof DXYRequestParam)
					{
						DXYRequestParam dxyRequestParam = (DXYRequestParam) annotation;
						String name = dxyRequestParam.value();
						if (!"".endsWith(name))
						{
							paramIndexMapping.put(name, i);
						}
					}
				}
			}

			Class<?>[] clazzs = method.getParameterTypes();
			for (int i = 0; i < clazzs.length; i++)
			{
				if (clazzs[i] == HttpServletRequest.class || clazzs[i] == HttpServletResponse.class)
				{
					paramIndexMapping.put(clazzs[i].getSimpleName(), i);
				}
			}
		}
	}

}
